Skip to content

feat(clickhouse): add StagingStore abstraction for GCS staging support#4200

Merged
whites11 merged 27 commits into
mainfrom
feat/gcs-staging-store
May 7, 2026
Merged

feat(clickhouse): add StagingStore abstraction for GCS staging support#4200
whites11 merged 27 commits into
mainfrom
feat/gcs-staging-store

Conversation

@whites11

@whites11 whites11 commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Resolves https://linear.app/clickhouse/issue/CLP-629/add-gcs-support-for-staging-bucket

Adds GCS support for ClickHouse CDC/snapshot staging, alongside the existing S3 path.

What changed

  • StagingStore interface (utils/staging.go) — abstracts Upload, TableFunctionExpr, DeletePrefix, Validate across cloud providers
  • S3StagingStore (utils/staging_s3.go) — extracts existing S3 logic, zero behavior change
  • GCSStagingStore (utils/staging_gcs.go) — native GCS SDK with Workload Identity auth; ClickHouse reads via url() table function with signed URLs
  • staging.go (clickhouse connector) — createStagingStore routes to S3 or GCS based on PEERDB_CLICKHOUSE_STAGING_PROVIDER env var
  • clickhouse.go — minimal diff: adds staging field, replaces inline S3 setup with createStagingStore() call, wraps session-token check in nil guard for non-S3 providers
  • avro_sync.go — calls store.Upload() directly instead of legacy WriteRecordsToS3
  • dynamicconf.goPeerDBClickHouseStagingBucketName falls back to legacy PEERDB_CLICKHOUSE_AWS_S3_BUCKET_NAME; adds PEERDB_CLICKHOUSE_STAGING_PROVIDER config

How GCS staging works

  1. PeerDB writes Avro files to GCS using the native Go SDK (Workload Identity on GKE)
  2. PeerDB generates a 15-minute signed URL for each staged file
  3. ClickHouse reads the file via url('https://storage.googleapis.com/...?X-Goog-Signature=...', 'Avro')

Env vars

Variable Default Description
PEERDB_CLICKHOUSE_STAGING_PROVIDER s3 s3 or gcs
PEERDB_CLICKHOUSE_STAGING_BUCKET_NAME (empty) Unified bucket name; falls back to PEERDB_CLICKHOUSE_AWS_S3_BUCKET_NAME for S3

Test plan

  • All existing unit tests pass (zero regression)
  • New unit tests for S3StagingStore (construction, table function expr, endpoint override, session tokens)
  • Tested on GCP dev: PG CDC pipe with GCS staging + directory TLS + GCP KMS — 5/5 CDC rows synced
  • Tested on AWS dev: PG CDC pipe with S3 staging + Lambda certs — confirmed no regression on existing AWS path

🤖 Generated with Claude Code

@whites11 whites11 self-assigned this Apr 20, 2026
@CLAassistant

CLAassistant commented Apr 20, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@codecov

codecov Bot commented Apr 20, 2026

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
2231 2 2229 203
View the top 2 failed test(s) by shortest run time
github.com/PeerDB-io/peerdb/flow/e2e::TestPeerFlowE2ETestSuitePG_CH_Cluster
Stack Traces | 0.01s run time
=== RUN   TestPeerFlowE2ETestSuitePG_CH_Cluster
=== PAUSE TestPeerFlowE2ETestSuitePG_CH_Cluster
=== CONT  TestPeerFlowE2ETestSuitePG_CH_Cluster
--- FAIL: TestPeerFlowE2ETestSuitePG_CH_Cluster (0.01s)
github.com/PeerDB-io/peerdb/flow/e2e::TestPeerFlowE2ETestSuitePG_CH_Cluster/Test_Binary_Format_Raw
Stack Traces | 140s run time
=== RUN   TestPeerFlowE2ETestSuitePG_CH_Cluster/Test_Binary_Format_Raw
=== PAUSE TestPeerFlowE2ETestSuitePG_CH_Cluster/Test_Binary_Format_Raw
=== CONT  TestPeerFlowE2ETestSuitePG_CH_Cluster/Test_Binary_Format_Raw
2026/05/07 10:17:45 INFO Received AWS credentials from peer for connector: ci x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/05/07 10:17:45 INFO Received AWS credentials from peer for connector: clickhouse x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
    clickhouse_test.go:1064: WaitFor waiting for CDC count 2026-05-07 10:17:50.45854231 +0000 UTC m=+598.672885725
    clickhouse_test.go:1068: WaitFor waiting for CDC count 2026-05-07 10:17:50.467868054 +0000 UTC m=+598.682211468
2026/05/07 10:17:51 INFO Executing and processing query x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,\"key\" FROM e2e_test_pgchcl_ywr2ioxm.\"test_nullengine\" ORDER BY id"
2026/05/07 10:17:51 INFO Executing and processing query stream x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,\"key\" FROM e2e_test_pgchcl_ywr2ioxm.\"test_nullengine\" ORDER BY id"
2026/05/07 10:17:51 INFO [pg_query_executor] declared cursor x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursorQuery="DECLARE peerdb_cursor_13398689585507344982 CURSOR FOR SELECT id,\"key\" FROM e2e_test_pgchcl_ywr2ioxm.\"test_nullengine\" ORDER BY id" args=[]
2026/05/07 10:17:51 INFO [pg_query_executor] fetching rows start x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,\"key\" FROM e2e_test_pgchcl_ywr2ioxm.\"test_nullengine\" ORDER BY id" channelLen=0
2026/05/07 10:17:51 INFO [pg_query_executor] fetching from cursor x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_13398689585507344982
2026/05/07 10:17:51 INFO processed row stream x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_13398689585507344982 records=3 bytes=26 channelLen=2
2026/05/07 10:17:51 INFO [pg_query_executor] fetched rows x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,\"key\" FROM e2e_test_pgchcl_ywr2ioxm.\"test_nullengine\" ORDER BY id" rows=3 bytes=26 channelLen=2
2026/05/07 10:17:51 INFO [pg_query_executor] fetching from cursor x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_13398689585507344982
2026/05/07 10:17:51 INFO processed row stream x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_13398689585507344982 records=0 bytes=0 channelLen=0
2026/05/07 10:17:51 INFO [pg_query_executor] fetched rows x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,\"key\" FROM e2e_test_pgchcl_ywr2ioxm.\"test_nullengine\" ORDER BY id" rows=0 bytes=0 channelLen=0
2026/05/07 10:17:51 INFO [pg_query_executor] committing transaction x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart
2026/05/07 10:17:51 INFO [pg_query_executor] committed transaction for query x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,\"key\" FROM e2e_test_pgchcl_ywr2ioxm.\"test_nullengine\" ORDER BY id" rows=3 bytes=26 channelLen=0
    clickhouse.go:76: 
        	Error Trace:	.../flow/e2e/congen.go:44
        	            				.../flow/e2e/clickhouse.go:76
        	            				.../flow/e2e/clickhouse.go:154
        	            				.../flow/e2e/test_utils.go:240
        	            				.../flow/e2e/test_utils.go:852
        	            				.../flow/e2e/test_utils.go:237
        	            				.../flow/e2e/clickhouse_test.go:1068
        	            				.../flow/e2e/clickhouse_test.go:1083
        	Error:      	Received unexpected error:
        	            	unable to establish connection with catalog: FATAL: terminating connection due to administrator command (SQLSTATE 57P01)
        	Test:       	TestPeerFlowE2ETestSuitePG_CH_Cluster/Test_Binary_Format_Raw
    clickhouse.go:107: begin tearing down postgres schema pgchcl_oycnvwc4
2026/05/07 10:18:05 INFO Executing and processing query x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_ui9n37xq.\"qrepapi_api_ui9n37xq\" ORDER BY id"
2026/05/07 10:18:05 INFO Executing and processing query stream x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_ui9n37xq.\"qrepapi_api_ui9n37xq\" ORDER BY id"
2026/05/07 10:18:05 INFO [pg_query_executor] declared cursor x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursorQuery="DECLARE peerdb_cursor_6103389777596105815 CURSOR FOR SELECT id,val FROM e2e_test_api_ui9n37xq.\"qrepapi_api_ui9n37xq\" ORDER BY id" args=[]
2026/05/07 10:18:05 INFO [pg_query_executor] fetching rows start x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_ui9n37xq.\"qrepapi_api_ui9n37xq\" ORDER BY id" channelLen=0
2026/05/07 10:18:05 INFO [pg_query_executor] fetching from cursor x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_6103389777596105815
2026/05/07 10:18:05 INFO processed row stream x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_6103389777596105815 records=2 bytes=19 channelLen=1
2026/05/07 10:18:05 INFO [pg_query_executor] fetched rows x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_ui9n37xq.\"qrepapi_api_ui9n37xq\" ORDER BY id" rows=2 bytes=19 channelLen=1
2026/05/07 10:18:05 INFO [pg_query_executor] fetching from cursor x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_6103389777596105815
2026/05/07 10:18:05 INFO processed row stream x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_6103389777596105815 records=0 bytes=0 channelLen=0
2026/05/07 10:18:05 INFO [pg_query_executor] fetched rows x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_ui9n37xq.\"qrepapi_api_ui9n37xq\" ORDER BY id" rows=0 bytes=0 channelLen=0
2026/05/07 10:18:05 INFO [pg_query_executor] committing transaction x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart
2026/05/07 10:18:05 INFO [pg_query_executor] committed transaction for query x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_ui9n37xq.\"qrepapi_api_ui9n37xq\" ORDER BY id" rows=2 bytes=19 channelLen=0
    clickhouse.go:107: 
        	Error Trace:	.../flow/e2e/pg.go:147
        	            				.../flow/e2e/clickhouse.go:107
        	            				.../flow/e2eshared/e2eshared.go:38
        	            				.../hostedtoolcache/go/1.26.2.../src/testing/testing.go:1317
        	            				.../hostedtoolcache/go/1.26.2.../src/testing/testing.go:1667
        	            				.../hostedtoolcache/go/1.26.2.../src/testing/testing.go:2030
        	            				.../hostedtoolcache/go/1.26.2.../src/runtime/panic.go:694
        	            				.../hostedtoolcache/go/1.26.2.../src/testing/testing.go:1022
        	            				.../flow/e2e/congen.go:44
        	            				.../flow/e2e/clickhouse.go:76
        	            				.../flow/e2e/clickhouse.go:154
        	            				.../flow/e2e/test_utils.go:240
        	            				.../flow/e2e/test_utils.go:852
        	            				.../flow/e2e/test_utils.go:237
        	            				.../flow/e2e/clickhouse_test.go:1068
        	            				.../flow/e2e/clickhouse_test.go:1083
        	            				.../hostedtoolcache/go/1.26.2.../src/reflect/value.go:586
        	            				.../hostedtoolcache/go/1.26.2.../src/reflect/value.go:369
        	            				.../flow/e2eshared/e2eshared.go:40
        	Error:      	failed to teardown postgres schema
        	Test:       	TestPeerFlowE2ETestSuitePG_CH_Cluster/Test_Binary_Format_Raw
        	Messages:   	pgchcl_oycnvwc4: failed to drop replication slots: ERROR: replication slot "peerflow_slot_ch_binary_format_raw_pgchcl_oycnvwc4" is active for PID 32398 (SQLSTATE 55006)
--- FAIL: TestPeerFlowE2ETestSuitePG_CH_Cluster/Test_Binary_Format_Raw (140.49s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@github-actions

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: Two unrelated flaky failures: a 1-nanosecond time precision mismatch in Test_Types_CH and a transient PostgreSQL catalog connection termination (SQLSTATE 57P01) in TestCancelTableAdditionRemoveAddRemove, both caused by CI environment non-determinism rather than code regressions.
Confidence: 0.95

✅ Automatically retrying the workflow

View workflow run

@github-actions

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The single failing test TestApiPg/TestPostgresTableOIDsMigration hit a PostgreSQL SQLSTATE 57P01 (admin_shutdown) error — the server forcibly terminated the connection, which is an infrastructure-level disruption unrelated to test logic and a classic sign of a flaky CI environment failure.
Confidence: 0.93

✅ Automatically retrying the workflow

View workflow run

@github-actions

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: Both failing tests (TestApiPg/TestTableAdditionWithoutInitialLoad and TestGenericChCluster_PG/Test_Custom_Replication_Slot_Starting_With_Numbers_CDC_Only) failed with PostgreSQL SQLSTATE 57P01 (admin_shutdown) — a transient catalog connection termination caused by the CI runner's Postgres service restarting, not a code bug.
Confidence: 0.95

✅ Automatically retrying the workflow

View workflow run

@github-actions

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The primary failure is a transient Temporal server connectivity timeout ("context deadline exceeded") causing test infrastructure to be unreachable, with a secondary BigQuery test timeout — both are classic flaky CI infrastructure failures unrelated to code changes.
Confidence: 0.9

✅ Automatically retrying the workflow

View workflow run

@github-actions

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The job failed during Docker container startup because port 49002 was already in use on the CI runner host (toxiproxy container networking error), a transient infrastructure issue unrelated to any code change.
Confidence: 0.97

✅ Automatically retrying the workflow

View workflow run

@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: Real build failure: createS3StagingStore signature was updated to require an extra string argument on the feat/gcs-staging-store branch, but the call site in connectors/clickhouse/clickhouse.go:111 was not updated.
Confidence: 0.99

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

@whites11 whites11 force-pushed the feat/gcs-staging-store branch from 84f57d7 to 53ab53d Compare May 1, 2026 04:27
@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: Deterministic compile error: createS3StagingStore signature was updated to require a new string argument on the feat/gcs-staging-store branch, but the call site at connectors/clickhouse/clickhouse.go:111 was not updated.
Confidence: 0.99

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

@whites11 whites11 force-pushed the feat/gcs-staging-store branch from 53ab53d to d631eef Compare May 1, 2026 04:37
@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The e2e test TestApiPg/TestTableAdditionWithoutInitialLoad failed due to a transient Postgres connection being forcibly terminated (SQLSTATE 57P01) and a race condition where a replication slot remained active during teardown — both are infrastructure timing issues unrelated to any code change.
Confidence: 0.92

✅ Automatically retrying the workflow

View workflow run

@whites11 whites11 force-pushed the feat/gcs-staging-store branch 3 times, most recently from 52ad279 to 496c92f Compare May 1, 2026 06:41
@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: Deterministic compile error: method peerDBOCFWriter.WriteRecordsToS3 is declared twice in connectors/utils/avro_writer.go (lines 96 and 170), introduced by the feat/gcs-staging-store branch.
Confidence: 0.99

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: Build fails with a Go compilation error — peerDBOCFWriter.WriteRecordsToS3 is declared twice in connectors/utils/avro_writer.go (lines 96 and 170), a deterministic duplicate method introduced on the feat/gcs-staging-store branch.
Confidence: 0.99

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The e2e test TestCancelTableAddition_NoRemovalAssumed failed because the PostgreSQL catalog connection was forcibly terminated by the server (FATAL: terminating connection due to administrator command, SQLSTATE 57P01), which is an infrastructure-level CI flake rather than a code defect.
Confidence: 0.92

✅ Automatically retrying the workflow

View workflow run

whites11 and others added 5 commits May 5, 2026 13:38
…ging

Introduce a StagingStore interface that abstracts cloud storage operations
for ClickHouse CDC/snapshot staging. This enables GCS support via signed
URLs and prepares for Azure Blob Storage support.

Changes:
- New StagingStore interface with Upload, TableFunctionExpr, DeletePrefix,
  Validate, and KeyPrefix methods
- S3StagingStore: extracts existing S3 logic (zero behavior change)
- GCSStagingStore: uses native GCS SDK with Workload Identity auth and
  ClickHouse url() table function with signed URLs
- WriteRecordsToStaging: cloud-agnostic Avro upload method
- PEERDB_CLICKHOUSE_STAGING_PROVIDER env var (s3 default, gcs)
- PEERDB_CLICKHOUSE_GCS_BUCKET_NAME env var for GCS bucket config

The S3 path is completely unchanged — GCS is additive only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Reorder S3StagingStore fields to fix fieldalignment (64→56 pointer bytes)
- Use require.Empty instead of require.Equal(t, "", ...) per testifylint
- Remove unused newValidationCheckKey function and its uuid import

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nv var

Add provider-agnostic PEERDB_CLICKHOUSE_STAGING_BUCKET_NAME that works
with PEERDB_CLICKHOUSE_STAGING_PROVIDER to configure staging storage.
GCS uses the unified var exclusively. S3 falls back to the legacy
PEERDB_CLICKHOUSE_AWS_S3_BUCKET_NAME when the unified var is not set.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The StagingStore-based upload method had the same name as the existing
S3-specific method, causing a compile error. Rename to WriteRecordsToStaging
to distinguish the generic staging interface from the legacy S3 path.
@whites11 whites11 force-pushed the feat/gcs-staging-store branch from 496c92f to 553bbb7 Compare May 5, 2026 11:43
whites11 added 2 commits May 5, 2026 14:50
Move createStagingStore, createS3StagingStore, and createGCSStagingStore
into a dedicated file to keep the clickhouse.go diff minimal. Remove
unused ValidateS3 and ValidateStaging functions.
…S3StagingStore

Pass ClickHouse version to createStagingStore so the S3-specific
session token validation lives alongside the S3 credential setup
rather than in NewClickHouseConnector.
@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The e2e test suite in the flow_test (ubuntu-latest-16-cores, 18, maria, 8.0, latest) job hit the 900-second timeout limit (ran for 900.715s), indicating tests hung or ran too slowly rather than failing with a logic error.
Confidence: 0.9

✅ Automatically retrying the workflow

View workflow run

whites11 added 2 commits May 5, 2026 15:36
…Staging

Remove the WriteRecordsToStaging wrapper from avro_writer.go and call
StagingStore.Upload directly in the ClickHouse connector. This avoids
adding a new method to avro_writer.go and keeps the upload logic
colocated with the ClickHouse-specific staging setup.
@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The e2e test suite hit the 900-second timeout limit (ran for 900.748s), indicating a timeout-induced failure rather than a code regression — all other test packages passed cleanly.
Confidence: 0.9

✅ Automatically retrying the workflow

View workflow run

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The test failed due to SQLSTATE 57P01 (PostgreSQL admin shutdown) — the catalog DB connection was forcibly terminated mid-run, which is a transient CI infrastructure issue unrelated to any code change.
Confidence: 0.97

✅ Automatically retrying the workflow

View workflow run

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: TestMongoClickhouseSuite/Test_Json_Disabled failed with ClickHouse error code 60 (UNKNOWN_TABLE) during the initial-load polling loop — the test queried the destination table before it was created, a classic e2e timing race condition.
Confidence: 0.85

✅ Automatically retrying the workflow

View workflow run

whites11 added 2 commits May 6, 2026 10:47
Merge newS3StagingStoreFromConfig + newS3StagingStore into one
newS3StagingStore. Same for GCS. Each staging_*.go file now has:
struct → constructor → methods. staging.go is just the router.
@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The test TestApiPg/TestTableAdditionWithoutInitialLoad failed during teardown due to a race condition: the test logic completed successfully, but cleanup couldn't drop the replication slot because a background replication worker process (PID 16388) still held it active when teardown ran.
Confidence: 0.92

✅ Automatically retrying the workflow

View workflow run

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The test failed due to a PostgreSQL catalog connection being forcibly terminated mid-run (FATAL: terminating connection due to administrator command, SQLSTATE 57P01), which is a transient CI infrastructure issue unrelated to the code changes.
Confidence: 0.93

✅ Automatically retrying the workflow

View workflow run

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: E2E tests failed due to Temporal client connection timeouts ("context deadline exceeded") and snapshot-stuck timing issues, with different tests failing on different runners and all running near the 900s timeout limit — classic signs of infrastructure flakiness rather than a code regression.
Confidence: 0.85

✅ Automatically retrying the workflow

View workflow run

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: Test_Types_CH fails due to a 1-microsecond rounding difference in QValueTime comparison (e.g. 9h20m44.867835s vs 9h20m44.867834s) that is time-dependent — the actual timestamp values differ across runs, indicating this is a precision edge case triggered by whatever microsecond boundary time.Now() lands on during the test, not a code regression.
Confidence: 0.82

✅ Automatically retrying the workflow

View workflow run

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The test e2e TestApiPg/TestCancelAddCancel failed due to FATAL: terminating connection due to administrator command (SQLSTATE 57P01) — a PostgreSQL admin shutdown killed the catalog connection mid-test, which is a transient CI infrastructure issue rather than a code bug.
Confidence: 0.92

✅ Automatically retrying the workflow

View workflow run

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: TestApiMongo/TestCancelAddCancel failed due to a transient infrastructure issue — the CI catalog PostgreSQL connection was terminated by an administrator command (SQLSTATE 57P01) mid-test, not a code defect.
Confidence: 0.95

✅ Automatically retrying the workflow

View workflow run

Comment thread flow/connectors/clickhouse/avro_sync.go Outdated
Comment thread flow/connectors/clickhouse/clickhouse.go

@jgao54 jgao54 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

…ouseS3Credentials

Per @jgao54 review on PR #4200:
- avro_sync.go: include runtime/debug.Stack() in the panic-recovery error so
  the stacktrace is preserved (#4200 (comment))
- utils/aws.go: delete now-unused ClickHouseS3Credentials struct
  (#4200 (comment))
@whites11 whites11 requested a review from a team as a code owner May 7, 2026 10:04
@claude

claude Bot commented May 7, 2026

Copy link
Copy Markdown

Code Review

1 issue found. Checked for bugs and CLAUDE.md compliance.

Bug: GCS storage.Client is never closed — resource leak

File: flow/connectors/clickhouse/staging_store.go (interface definition) / flow/connectors/clickhouse/staging_gcs.go (line 51)

The gcsStagingStore creates a *storage.Client in newGCSStagingStore, but this client is never closed. The StagingStore interface has no Close() method, and ClickHouseConnector.Close() only closes the ClickHouse database connection.

The GCS storage.Client holds HTTP connections and background goroutines that should be released (its godoc explicitly states the caller must call Close). Each ClickHouseConnector created with staging_provider=gcs will leak a client. The S3 path does not have this issue since it creates ephemeral S3 clients on demand.

Suggested fix: Add a Close() error method to the StagingStore interface, implement it on gcsStagingStore to call g.client.Close(), add a no-op on s3StagingStore, and call c.staging.Close() from ClickHouseConnector.Close().

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The e2e test suite hit the 15-minute global timeout (panic: test timed out after 15m0s), killing all running tests — multiple tests failed with nearly identical durations (~31s or ~184s) indicating they were all blocked on per-test wait deadlines, consistent with infrastructure-level flakiness or resource contention rather than a deterministic code bug.
Confidence: 0.75

✅ Automatically retrying the workflow

View workflow run

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The e2e test TestApiPg/TestTableAdditionWithoutInitialLoad failed due to a transient PostgreSQL catalog connection termination (SQLSTATE 57P01: admin_shutdown), not a code defect — the DB backend was killed externally mid-test, likely by a CI runner resource limit.
Confidence: 0.93

✅ Automatically retrying the workflow

View workflow run

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The e2e test TestCancelTableAdditionRemoveAddRemove failed due to a transient PostgreSQL catalog connection being forcibly terminated (SQLSTATE 57P01 / admin_shutdown), not a code bug.
Confidence: 0.85

✅ Automatically retrying the workflow

View workflow run

@whites11 whites11 merged commit be2e1ba into main May 7, 2026
19 of 23 checks passed
@whites11 whites11 deleted the feat/gcs-staging-store branch May 7, 2026 11:14
dtunikov added a commit that referenced this pull request Jun 19, 2026
## Summary

Restores the ability to retain ClickHouse staging avro files after a
snapshot/QRep flow completes, addressing a silent behavior change
introduced in #4200.

## Background — the regression

#4200 (`StagingStore` abstraction) refactored the ClickHouse connector
so avro **upload** and staging **cleanup** both key off the staging
store's real bucket prefix (`staging.KeyPrefix()`). The PR was described
as "zero behavior change" for the S3 path, but the cleanup path actually
changed behavior:

- **Before:** `CleanupQRepFlow` keyed off `config.StagingPath` (=
`SnapshotStagingPath`), guarded by `strings.HasPrefix(stagingPath,
"s3://")`. For ClickHouse mirrors, `SnapshotStagingPath` is typically
empty (the bucket comes from `PEERDB_CLICKHOUSE_AWS_S3_BUCKET_NAME`), so
the guard was false and **cleanup was a no-op** — staging avro files
were retained in the bucket.
- **After:** cleanup keys off the same real prefix that uploads use, so
it now always deletes `<staging-prefix>/<flowJobName>` once the flow
completes.

Deployments that upgraded from 0.36.18 → 0.36.24 and had downstream
pipelines consuming those staging avro artifacts saw the files disappear
as soon as the mirror's snapshot completed.

## Change

Cleanup remains the default — it is the correct behavior and avoids
unbounded staging growth. This PR adds an opt-out:

- New dynconf setting `PEERDB_CLICKHOUSE_SKIP_STAGING_CLEANUP` (bool,
default `false`).
- When enabled, `CleanupQRepFlow` logs and returns early without
deleting staging objects.

## Notes

- The staging bucket is PeerDB-internal scratch space; relying on
retained files is fragile. The recommended long-term path for consuming
avro artifacts is a dedicated PG→S3 QRep mirror. This flag is an escape
hatch for affected deployments.
- Separately, there is a latent mismatch when `PEERDB_S3_UUID_PREFIX` is
enabled: upload keys become `<prefix>/<uuid>/<flowJobName>/...` while
cleanup deletes `<prefix>/<flowJobName>`, so cleanup misses those
objects. Out of scope here but worth tracking.

## Test plan

- [x] `go build ./connectors/clickhouse/... ./internal/...`
- [x] `go vet ./connectors/clickhouse/`
- [ ] Manual: run a CH snapshot with
`PEERDB_CLICKHOUSE_SKIP_STAGING_CLEANUP=true` and confirm staging avro
files remain after completion; with default, confirm they are deleted.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GCP: Support Getting Creds from GCP/GKE Workload Identity GCP: Support GCS natively in PeerDB

4 participants